#include "StdAfx.h"
#include "MLConsoleMovStream.h"
#include "MLConsolePrinter.h"

CMLConsoleMovStream::CMLConsoleMovStream(void)
{
}

CMLConsoleMovStream::~CMLConsoleMovStream(void)
{
}

int CMLConsoleMovStream::Run(const std::vector<std::wstring> &vCommand)
{
	int nRet;
	int nComIndex = 1;
	if (vCommand.size() < 15)
	{
		_ASSERT(false);
		return -1;
	}
	m_wstrInputFileName  = vCommand[nComIndex++];
	m_wstrOutputFileName = vCommand[nComIndex++];
	m_nInitNumClusters  = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamBufferSize  = _wtoi(vCommand[nComIndex++].c_str());
	m_nStreamMaxLength = _wtoi(vCommand[nComIndex++].c_str());
	m_nHorizonLength = _wtoi(vCommand[nComIndex++].c_str());
	m_nStatLength = _wtoi(vCommand[nComIndex++].c_str());
	m_nInitNumber = _wtoi(vCommand[nComIndex++].c_str());
	m_dMaxRadius = _wtof(vCommand[nComIndex++].c_str());
	m_dMaxAge = _wtof(vCommand[nComIndex++].c_str());
	m_nNumLastTimeCheck  = _wtoi(vCommand[nComIndex++].c_str());
	m_dMaxDriftFactor = _wtof(vCommand[nComIndex++].c_str());
	m_dStretchFactor = _wtof(vCommand[nComIndex++].c_str());
	m_dMergeFactor = _wtof(vCommand[nComIndex++].c_str());
	m_dMinDataPointNumberRatio  = _wtof(vCommand[nComIndex++].c_str());
	m_nEventCheckInterval = _wtoi(vCommand[nComIndex++].c_str());

	m_bDisplayClusterEvent = false;
	m_bDisplayHorzionSSQ = false;
	m_bDisplayRawFormat = false;
	m_bDisplayOnlyTime = false;
	while ((int)vCommand.size() > nComIndex)
	{
		std::wstring wszDisplay = vCommand[nComIndex++];
		if (_wcsicmp(wszDisplay.c_str(), L"displayevent") == 0)
			m_bDisplayClusterEvent = true;
		else if(_wcsicmp(wszDisplay.c_str(), L"displayhorizon") == 0)
			m_bDisplayHorzionSSQ = true;
		else if(_wcsicmp(wszDisplay.c_str(), L"displayrawformat") == 0)
			m_bDisplayRawFormat = true;
		else if(_wcsicmp(wszDisplay.c_str(), L"displayonlytime") == 0)
			m_bDisplayOnlyTime = true;
	}
	
	CMLConsolePrinter::PrintConsole(L"MovStream starts.\n");

	// Initialize the MovStream
	nRet = InitMovStream();
	ML_ON_FAILED_RETURN(nRet);

	// Initialize the data stream loader
	nRet = InitStreamLoader();
	ML_ON_FAILED_RETURN(nRet);

	// Initialize the result output file
	nRet = InitOutputFile();
	ML_ON_FAILED_RETURN(nRet);

	// Run MovStream
	nRet = RunMovStream();
	ML_ON_FAILED_RETURN(nRet);

	// Close 
	CloseAll();

	CMLConsolePrinter::PrintConsole(L"MovStream finishes.\n");
	return 0;
}

int CMLConsoleMovStream::RunMovStream(void)
{
	int nRet;
	int i;
	long nStartClock;
	long nCurrentClock;

	// Process data
	mylablib::CMLDataSet dataSet;
	mylablib::CMLDataSet horizonDataSet;
	std::vector<mylablib::CMLDataCluster> vClusters;
	std::vector<mylablib::CMLDataPoint> vCenters;

	nStartClock = mylablib::CMLTools::GetCurrentClock();
	nRet = m_StreamLoader.Load(dataSet, m_nStreamBufferSize);
	if (ML_FAILED(nRet))
	{
		m_OutputFile.close();
		m_StreamLoader.Close();
		_ASSERT(false);
		return nRet;
	}

	int nDataIndex = 0;
	double dAvgSSQ = 0.0;
	double dSSQ;
	int nStatCount = 0;
	int nHorizonLen = m_nHorizonLength;
	int nHorizonIndex=0;
	horizonDataSet.Resize(nHorizonLen);

	while (dataSet.Count() > 0 )
	{
		int nPointIndex;
		for (nPointIndex=0; nPointIndex<dataSet.Count(); nPointIndex++)
		{
			m_MovStream.Insert(dataSet.Get(nPointIndex));
			nDataIndex++;

			if (nDataIndex < m_nInitNumber)
				continue;

			horizonDataSet.Set(nHorizonIndex, dataSet.Get(nPointIndex));
			nHorizonIndex++;
			if (nHorizonIndex == m_nHorizonLength)
			{
				nHorizonIndex = 0;
				CMLConsolePrinter::PrintConsole(L"Data Index : %d\n",nDataIndex);

				// Create macro-clusters;
				m_MovStream.GetClusters(L"", vClusters);
				
				// Collect the centers of macro-clusters
				vCenters.resize(vClusters.size());
				for (i=0; i<(int)vClusters.size(); i++)
					vCenters[i] = vClusters[i].center;

				if (m_bDisplayOnlyTime)
				{
					nCurrentClock = mylablib::CMLTools::GetCurrentClock();
					m_OutputFile << nDataIndex << L','<< nCurrentClock - nStartClock << L"\n";
				}
				else
				{
					// Calculate the SSQ
					dSSQ = mylablib::CMLTools_Stat::CalSSQ(horizonDataSet, vCenters, m_MovStream.GetDistanceFunc());
					dAvgSSQ += dSSQ;

					if (m_bDisplayHorzionSSQ)
					{
						// Output the SSQ
						nCurrentClock = mylablib::CMLTools::GetCurrentClock();
						if (m_bDisplayRawFormat)
						{
							m_OutputFile << nDataIndex << L','<< dSSQ << L',' << nCurrentClock - nStartClock << L"\n";
						}
						else
						{
							m_OutputFile << L"SSQ : "<< dSSQ << L'(' << horizonDataSet.Count() << L")(" << vCenters.size() << L") ";
							m_OutputFile << L"Time: " << nCurrentClock - nStartClock << L"\n";
						}
					}
					else
					{
						nStatCount++;
						if (nStatCount == m_nStatLength)
						{
							dAvgSSQ /= (double)m_nStatLength;
							// Output the Average SSQ
							nCurrentClock = mylablib::CMLTools::GetCurrentClock();
							if (m_bDisplayRawFormat)
							{
								m_OutputFile << nDataIndex << L','<< dAvgSSQ << L',' << nCurrentClock - nStartClock << L"\n";
							}
							else
							{
								m_OutputFile << nDataIndex << L": AverageSSQ : "<< dAvgSSQ << L" Time: " << nCurrentClock - nStartClock << L"\n";
							}
							std::wcout<< nDataIndex << L": AverageSSQ : "<< dAvgSSQ << L" Time: " << nCurrentClock - nStartClock <<L"\n";
							nStatCount = 0;
							dAvgSSQ = 0.0;
						}
					}
				}
			}

			if (nDataIndex % m_nEventCheckInterval == 0)
			{
				std::vector<CMLMovStreamClusterEvent> vEvents;
				m_MovStream.CheckClustersEvents(vEvents);
				if (m_bDisplayClusterEvent)
				{
					if (vEvents.size() > 0)
						m_OutputFile << nDataIndex << L' ';
					OutputClusterEvents(vEvents);
				}
			}

		}

		// Reload the data buffer
		nRet = m_StreamLoader.Load(dataSet, m_nStreamBufferSize);
		if (ML_FAILED(nRet))
		{
			m_OutputFile.close();
			m_StreamLoader.Close();
			_ASSERT(false);
			return nRet;
		}
	}

	return 0;
}


int CMLConsoleMovStream::InitMovStream(void)
{
	// Initialize the MovStream
	m_MovStream.SetInitNumber(m_nInitNumber);
	m_MovStream.SetNumInitClusters(m_nInitNumClusters);
	m_MovStream.SetMaxNumClusters(m_nInitNumClusters);
	m_MovStream.SetMinNumClusters(m_nInitNumClusters);
	m_MovStream.SetMaxRadius(m_dMaxRadius);
	m_MovStream.SetMaxAge(m_dMaxAge);
	m_MovStream.SetNumLastTimeStampCheck(m_nNumLastTimeCheck);
	m_MovStream.SetMaxDriftFactor(m_dMaxDriftFactor);
	m_MovStream.SetMergeFactor(m_dMergeFactor);
	m_MovStream.SetStretchFactor(m_dStretchFactor);
	m_MovStream.SetMinDataPointNumberRatio(m_dMinDataPointNumberRatio);
	m_MovStream.SetTotalSWLength(m_nHorizonLength);

	return 0;
}

int CMLConsoleMovStream::InitStreamLoader(void)
{
	// Load the input dataset
	int nRet = m_StreamLoader.Initialize(m_wstrInputFileName,0 ,m_nStreamMaxLength);
	if (ML_FAILED(nRet))
	{
		CMLConsolePrinter::PrintError(L"Cannot open the input dataset file : %s\n", m_wstrInputFileName.c_str());
		_ASSERT(false);
		return nRet;
	}
	return 0;
}

int CMLConsoleMovStream::InitOutputFile(void)
{
	m_OutputFile.open(m_wstrOutputFileName.c_str());
	if (m_OutputFile.fail())
	{
		_ASSERT(false);
		return -1;
	}
	return 0;
}

int CMLConsoleMovStream::CloseAll(void)
{
	m_StreamLoader.Close();
	m_OutputFile.close();
	return 0;
}

int CMLConsoleMovStream::OutputClusters()
{
	int i;
	std::vector<mylablib::CMLDataCluster> vClusters;
	m_MovStream.GetClusters(L"", vClusters);

	double dSSQ = mylablib::CMLTools_Stat::CalSSQ(vClusters, m_MovStream.GetDistanceFunc());
	int nTotalCount = 0;;
	for (i=0; i<(int)vClusters.size(); i++)
		nTotalCount += vClusters[i].dataset.Count();

	m_OutputFile << L"SSQ : "<< dSSQ << L'(' << nTotalCount << L")(" << (int)vClusters.size() << L")\n";
	for (i=0; i<(int)vClusters.size(); i++)
	{
		m_OutputFile << vClusters[i].center.ToWString() << L" : ";
	}
	m_OutputFile << L'\n';
	return 0;
}

int CMLConsoleMovStream::OutputClusterEvents(const std::vector<CMLMovStreamClusterEvent>& vEvents)
{
	int nEventIndex;
	if (vEvents.size() == 0)
		return 0;

	for (nEventIndex=0; nEventIndex<(int)vEvents.size(); nEventIndex++)
	{
		m_OutputFile << L' ' << vEvents[nEventIndex].ToWString();
		if (nEventIndex < (int)vEvents.size() - 1)
		{
			m_OutputFile << L" : ";
		}
	}
	m_OutputFile << L'\n';
	return 0;
}
